/*******************************************************************/
/* @ All Rights Reserved Copyright (C) 2014 broada,Co.,Ltd.        */
/*                                                                 */
/*  NAME      = frstat_system.c                                    */
/*  NOTE      =                                                    */
/*  DATE      = 2015/01/14 by guojq                                */
/*******************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/time.h>
#include <pthread.h>
#include <nids.h>

#include "frcomm.h"
#include "frmem.h"
#include "frlog.h"
#include "frlist.h"
#include "frhash2.h"
#include "frame.h"
#include "frame_params.h"
#include "frreport_mng.h"

extern int g_i_nids_threads;
extern struct nids_ip_tcp_stream g_st_nids_ip_tcp_stream[APM_DEV_MAX];

#define STAT_L4_HASH_SIZE   10000
//timeout 过期时间(秒   60*60)， 1小时
#define STAT_TIME_OUT     3600
//超过多少秒的数据不上报(10秒)
#define STAT_NO_REPORT_TIME   10

struct l4_status {
    unsigned long ul_tx_bytes;     //发送流量
    unsigned long ul_rx_bytes;     //接受流量
    unsigned long ul_conns;        //连接数(非累加值)
    unsigned long ul_acks;         //ack数量
    unsigned long ul_resets;       //reset数量
    unsigned long ul_zero_win;     //  零窗口数

    unsigned long  ul_direct_known;  //确定方向(有syn，非累加值)
    struct timeval st_time_update;   //最后更新时间
};

struct l4_status_info {
    unsigned long  ul_seq;           //序列号(用于区分是否为当前更新批次)
    unsigned short us_proto;    //协议tcp
    unsigned int   ui_ip;       //服务端ip
    unsigned short us_port;     //服务端port

    struct l4_status st_l4_status_now;  //当前
    struct l4_status st_l4_status_old;  //上一次
};

FR_STATIC int g_i_flag_init = FR_FAIL;
FR_STATIC int g_i_flag_first = FR_YES;
FR_STATIC unsigned long g_ul_seq = 0;
//FR_STATIC pthread_rwlock_t g_l4_rwlock;
FR_STATIC Frhash_header g_st_l4_stat;      //模块信息hash

/*******************************************************************************
*  FUNC     :  释放节点
*  ARGS     :  p_node
*  RTN      :
*  NOTE     :
*******************************************************************************/
FR_STATIC void __stat_l4_node_free(Frlist_node* p_node)
{
    l4_status *p_stat_node = NULL;

    if (p_node && p_node->data)
    {
        p_stat_node = (l4_status *)p_node->data;
        //if(p_stat_node->p_module_name)
        //    frfree(p_stat_node->p_module_name);
        frfree(p_stat_node);
    }

    return;
}

/*******************************************************************************
*  FUNC     :  生成hash key
*  ARGS     :
*  RTN      :
*  NOTE     :
*******************************************************************************/
FR_STATIC int __frstat_l4hash_key(u_short us_proto, u_int ui_ip, u_short us_port, char *p_key, unsigned int ui_key_size)
{
    int i_idx=0, i_len;
    u_int *stupid_strict_aliasing_warnings=(u_int*)p_key;

    if(p_key == NULL || ui_key_size <= sizeof(u_int)+ sizeof(u_short)*2 )
    {
        FRLOG_ERROR_DETAIL("__frstat_l4hash_key err");
        return 0;
    }

    i_len = sizeof(u_int);
    *stupid_strict_aliasing_warnings = ui_ip;
    i_idx += i_len;

    i_len = sizeof(u_short);
    *(u_short *) (p_key + i_idx) = us_port;
    i_idx += i_len;

    *(u_short *) (p_key + i_idx) = us_proto;
    i_idx += i_len;

    return i_idx;
}

/*******************************************************************************
*  FUNC     :  数据增加到report体里
*  ARGS     :
*  RTN      :
*  NOTE     :
*******************************************************************************/
FR_STATIC void __frstat_add_report_data(Frreport_data *p_st_report_data, struct l4_status_info *p_st_status_info)
{
    Frreport_body_data *p_st_report_body=NULL;
    unsigned long ul_tx_bytes;
    unsigned long ul_rx_bytes;
    unsigned long ul_conns;
    unsigned long ul_acks;
    unsigned long ul_resets;
    unsigned long ul_zero_win;
    unsigned long ul_time;

    if(p_st_report_data == NULL || p_st_status_info == NULL || p_st_status_info->st_l4_status_old.st_time_update.tv_sec == 0)
        return;

    ul_time = (p_st_status_info->st_l4_status_now.st_time_update.tv_sec - p_st_status_info->st_l4_status_old.st_time_update.tv_sec)*1000000
              + p_st_status_info->st_l4_status_now.st_time_update.tv_usec - p_st_status_info->st_l4_status_old.st_time_update.tv_usec;
    if(ul_time == 0)
        return;

    if( p_st_status_info->st_l4_status_now.ul_rx_bytes >= p_st_status_info->st_l4_status_old.ul_tx_bytes)
        ul_tx_bytes = (p_st_status_info->st_l4_status_now.ul_tx_bytes - p_st_status_info->st_l4_status_old.ul_tx_bytes) * 1000000 / ul_time;
    else
        ul_tx_bytes = 0;
    if(p_st_status_info->st_l4_status_now.ul_rx_bytes >= p_st_status_info->st_l4_status_old.ul_rx_bytes)
        ul_rx_bytes = (p_st_status_info->st_l4_status_now.ul_rx_bytes - p_st_status_info->st_l4_status_old.ul_rx_bytes) * 1000000 / ul_time;
    else
        ul_rx_bytes = 0;
    ul_conns = p_st_status_info->st_l4_status_now.ul_conns;
    if(p_st_status_info->st_l4_status_now.ul_acks >= p_st_status_info->st_l4_status_old.ul_acks)
        ul_acks = (p_st_status_info->st_l4_status_now.ul_acks - p_st_status_info->st_l4_status_old.ul_acks) * 1000000 / ul_time;
    else
        ul_acks = 0;
    if(p_st_status_info->st_l4_status_now.ul_resets >= p_st_status_info->st_l4_status_old.ul_resets)
        ul_resets = ( p_st_status_info->st_l4_status_now.ul_resets - p_st_status_info->st_l4_status_old.ul_resets) * 1000000 / ul_time;
    else
        ul_resets = 0;
    if(p_st_status_info->st_l4_status_now.ul_zero_win >= p_st_status_info->st_l4_status_old.ul_zero_win)
        ul_zero_win = ( p_st_status_info->st_l4_status_now.ul_zero_win - p_st_status_info->st_l4_status_old.ul_zero_win ) * 1000000 / ul_time;
    else
        ul_zero_win = 0;
    p_st_report_body = frreport_body_new_stat_l4_tcp(p_st_status_info->us_proto
                                                    , p_st_status_info->ui_ip
                                                    , p_st_status_info->us_port
                                                    , ul_tx_bytes
                                                    , ul_rx_bytes
                                                    , ul_conns
                                                    , ul_acks
                                                    , ul_resets
                                                    , ul_zero_win);
    if(p_st_report_body)
        frreport_data_add_body(p_st_report_data, p_st_report_body);

    return;
}

/*******************************************************************************
*  FUNC     :  上报数据加入p_st_report_data里
*  ARGS     :
*  RTN      :
*  NOTE     :
*******************************************************************************/
FR_STATIC void __frstat_l4_report(Frreport_data *p_st_report_data, Frhash_header *p_hash_hdr, unsigned long ul_seq, long l_timeout)
{
    unsigned int i_loop=0;
    Frlist_header *p_st_lst_hdr = NULL;
    Frlist_node *p_st_node=NULL, *p_st_node_del=NULL;
    struct l4_status_info *p_st_status_info=NULL;
    struct timeval st_timeval_now;
    long l_time_diff=0;

    if(p_st_report_data == NULL || p_hash_hdr == NULL)
        return;

    gettimeofday (&st_timeval_now, NULL);
    FRHASH_LOOP(i_loop, p_hash_hdr, p_st_lst_hdr)
    {
        p_st_node = FRLIST_FIRST_NODE(p_st_lst_hdr);
        while(p_st_node)
        {
            p_st_node_del = NULL;
            if(p_st_node && p_st_node->data)
            {
                p_st_status_info = (struct l4_status_info *)(p_st_node->data);
                l_time_diff = st_timeval_now.tv_sec - p_st_status_info->st_l4_status_now.st_time_update.tv_sec;
                if(l_time_diff >= l_timeout)
                    p_st_node_del = p_st_node;

                //加入上报数据,超过10秒数据不上报
                if(ul_seq == p_st_status_info->ul_seq && l_time_diff < STAT_NO_REPORT_TIME)
                    __frstat_add_report_data(p_st_report_data, p_st_status_info);

            }
            p_st_node = FRLIST_NEXT_NODE(p_st_node);
            //检查过期数据
            if(p_st_node_del)
                frhash2_del(p_hash_hdr, p_st_node_del, __stat_l4_node_free);
        }
    }

    return;
}

/*******************************************************************************
*  FUNC     :  生成上报数据
*  ARGS     :
*  RTN      :
*  NOTE     :
*******************************************************************************/
void frstat_l4_report(void)
{
    char arr_key[20];
    int i_key_len=0, i_ret_fun=0, i_flag_update=FR_NO;
    int i_loop = 0, j_loop=0;
    struct nids_ip_tcp_stream *p_tcp_stream = NULL;
    struct nids_stat_list *p_nids_tcp_stat=NULL;
    Frlist_node *p_st_node_lookup=NULL;
    struct l4_status_info *p_st_l4_data_info=NULL;
    Frreport_data *p_st_report_data=NULL;

    g_ul_seq++;
    //数据保存并计算
    for(i_loop=0; i_loop<g_i_nids_threads && i_loop < APM_DEV_MAX ;i_loop++)
    {
        p_tcp_stream = &g_st_nids_ip_tcp_stream[i_loop];

        for (j_loop=0;j_loop < NIDS_STAT_HASH_SIZE;j_loop++)
        {
            p_nids_tcp_stat = p_tcp_stream->st_nids_stat.st_stat_hash[j_loop];

            while (p_nids_tcp_stat)
            {
                if (p_nids_tcp_stat->daddr == 0)
                {
                    p_nids_tcp_stat = p_nids_tcp_stat->next;
                    continue;
                }

                i_key_len = __frstat_l4hash_key(IPPROTO_TCP, p_nids_tcp_stat->daddr, p_nids_tcp_stat->dest, arr_key, sizeof(arr_key));
                if(g_i_flag_first == FR_NO)
                    p_st_node_lookup = frhash2_getStr(&g_st_l4_stat, arr_key, i_key_len);
                else
                    p_st_node_lookup = NULL;

                //增加节点
                if(p_st_node_lookup == NULL)
                {
                    p_st_l4_data_info = (struct l4_status_info *) frmalloc(sizeof(struct l4_status_info));
                    if(p_st_l4_data_info == NULL)
                    {
                        FRLOG_ERROR_DETAIL("frmalloc err");
                        break;
                    }
                    memset(p_st_l4_data_info, 0x00, sizeof(struct l4_status_info));
                    p_st_l4_data_info->ul_seq = g_ul_seq;
                    p_st_l4_data_info->us_proto = IPPROTO_TCP;
                    p_st_l4_data_info->ui_ip = p_nids_tcp_stat->daddr;
                    p_st_l4_data_info->us_port = p_nids_tcp_stat->dest;
                    p_st_l4_data_info->st_l4_status_now.ul_tx_bytes = p_nids_tcp_stat->tx;
                    p_st_l4_data_info->st_l4_status_now.ul_rx_bytes = p_nids_tcp_stat->rx;
                    p_st_l4_data_info->st_l4_status_now.ul_conns = p_nids_tcp_stat->st_stat.conns;
                    p_st_l4_data_info->st_l4_status_now.ul_acks = p_nids_tcp_stat->st_stat.acks;
                    p_st_l4_data_info->st_l4_status_now.ul_resets = p_nids_tcp_stat->st_stat.resets;
                    p_st_l4_data_info->st_l4_status_now.ul_zero_win = p_nids_tcp_stat->st_stat.tcp_zero_windows;
                    p_st_l4_data_info->st_l4_status_now.ul_direct_known = p_nids_tcp_stat->st_stat.direct_known;
                    gettimeofday(&p_st_l4_data_info->st_l4_status_now.st_time_update, NULL);

                    p_st_node_lookup = frlist_node_create(p_st_l4_data_info);
                    if(p_st_node_lookup == NULL)
                    {
                        if(p_st_l4_data_info)
                            frfree(p_st_l4_data_info);
                        FRLOG_ERROR_DETAIL("frlist_node_create err");
                        break;
                    }
                    i_ret_fun = frhash2_addStr(&g_st_l4_stat, arr_key, i_key_len, p_st_node_lookup);
                    if(i_ret_fun == FR_FAIL)
                    {
                        frfree(p_st_l4_data_info);
                        frfree(p_st_node_lookup);
                        FRLOG_ERROR_DETAIL("frhash2_addStr err");
                        break;
                    }
                }
                else //更新节点
                {
                    p_st_l4_data_info = (struct l4_status_info *) FRLIST_GET_NODE_DATA(p_st_node_lookup);
                    if(p_st_l4_data_info == NULL)
                    {
                        p_nids_tcp_stat = p_nids_tcp_stat->next;
                        continue;
                    }
                    if( p_st_l4_data_info->ul_seq != g_ul_seq)
                    {
                        p_st_l4_data_info->ul_seq = g_ul_seq;
                        memcpy(&p_st_l4_data_info->st_l4_status_old, &p_st_l4_data_info->st_l4_status_now, sizeof(struct l4_status));
                        p_st_l4_data_info->st_l4_status_now.ul_tx_bytes = p_nids_tcp_stat->tx;
                        p_st_l4_data_info->st_l4_status_now.ul_rx_bytes = p_nids_tcp_stat->rx;
                        p_st_l4_data_info->st_l4_status_now.ul_conns = p_nids_tcp_stat->st_stat.conns;
                        p_st_l4_data_info->st_l4_status_now.ul_acks = p_nids_tcp_stat->st_stat.acks;
                        p_st_l4_data_info->st_l4_status_now.ul_resets = p_nids_tcp_stat->st_stat.resets;
                        p_st_l4_data_info->st_l4_status_now.ul_zero_win = p_nids_tcp_stat->st_stat.tcp_zero_windows;
                        p_st_l4_data_info->st_l4_status_now.ul_direct_known = p_nids_tcp_stat->st_stat.direct_known;
                        gettimeofday(&(p_st_l4_data_info->st_l4_status_now.st_time_update), NULL);
                    }
                    else
                    {
                        p_st_l4_data_info->st_l4_status_now.ul_tx_bytes += p_nids_tcp_stat->tx;
                        p_st_l4_data_info->st_l4_status_now.ul_rx_bytes += p_nids_tcp_stat->rx;
                        p_st_l4_data_info->st_l4_status_now.ul_conns += p_nids_tcp_stat->st_stat.conns;
                        p_st_l4_data_info->st_l4_status_now.ul_acks += p_nids_tcp_stat->st_stat.acks;
                        p_st_l4_data_info->st_l4_status_now.ul_resets += p_nids_tcp_stat->st_stat.resets;
                        p_st_l4_data_info->st_l4_status_now.ul_zero_win += p_nids_tcp_stat->st_stat.tcp_zero_windows;
                        p_st_l4_data_info->st_l4_status_now.ul_direct_known += p_nids_tcp_stat->st_stat.direct_known;
                    }
                    i_flag_update = FR_YES;
                }

                p_nids_tcp_stat = p_nids_tcp_stat->next;
            } //end while

        }//end j_loop

    }//end i_loop

    //第一次不上报
    if(g_i_flag_first == FR_YES)
    {
        g_i_flag_first = FR_NO;
        return;
    }

    //有没有更新数据
    if(i_flag_update==FR_NO)
        return;

    //数据上报
    p_st_report_data = frreport_data_stat_l4_new();
    if(p_st_report_data == NULL)
        return;

    __frstat_l4_report(p_st_report_data, &g_st_l4_stat, g_ul_seq, STAT_TIME_OUT);
    frreport_mng_save_report_data2(&p_st_report_data);

    return;
}

/*******************************************************************************
*  FUNC     :  初始化l4的信息
*  ARGS     :
*  RTN      :  FR_SUCC   (成功)
*              FR_FAIL   (失败)
*  NOTE     :
*******************************************************************************/
int frstat_l4_init(void)
{
    //if (pthread_rwlock_init(&g_l4_rwlock, NULL) != 0)
    //{
    //    FRLOG_ERROR_DETAIL("pthread_rwlock_init failed");
    //    return FR_FAIL;
    //}
    
    if (frhash2_create(&g_st_l4_stat, STAT_L4_HASH_SIZE) != FR_SUCC)
    {
        FRLOG_ERROR_DETAIL("frhash2_create failed");
        return FR_FAIL;
    }
    g_i_flag_init = FR_SUCC;
    
    return FR_SUCC;
}

/*******************************************************************************
*  FUNC     :  释放资源
*  ARGS     :
*  RTN      :
*  NOTE     :
*******************************************************************************/
void frstat_l4_destroy(void)
{
    if(g_i_flag_init == FR_FAIL)
        return;

    //pthread_rwlock_wrlock(&g_l4_rwlock);
    frhash2_free(&g_st_l4_stat, __stat_l4_node_free);
    //pthread_rwlock_unlock(&g_l4_rwlock);
    
    //pthread_rwlock_destroy(&g_l4_rwlock);

    g_i_flag_init = FR_FAIL;
    return;
}

